package face.pay.common.withdraw.impl;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import face.pay.common.withdraw.IWithdrawManageService;
import face.pay.dao.*;
import face.pay.entity.*;
import face.pay.param.withdraw.TransferList;
import face.pay.param.withdraw.WithdrawParam;
import face.pay.util.*;
import face.pay.util.page.PageData;
import face.pay.util.page.PageParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class WithdrawManageService implements IWithdrawManageService {


    @Value("${inside_outapi.server}")
    private String inside_outapi;

    @Value("${yemadai.server.withdraw}")
    private String url;

    @Value("${yemadai.server.withdraw.callback}")
    private String callbackUrl;


    @Autowired
    private CashRecodeInfoDao withdrawOrderDao;


    @Autowired
    private UserAccountDao userAccountDao;


    @Autowired
    private UserBankInfoDao userBankInfoDao;


    @Autowired
    private OemAccountConfigDao oemAccountConfigDao;


    @Autowired
    private InterfaceRequestLogDao interfaceRequestLogDao;


    /**
     * 分页查询用户提现记录
     *
     * @param withdrawOrderInfo
     * @param pageParam
     * @param loginUser
     * @return
     */
    @Override
    public PageData queryData(CashRecodeInfo withdrawOrderInfo, PageParam pageParam, UserInfo loginUser) {
        PageHelper.startPage(pageParam.getPageNo(), pageParam.getPageSize());
        withdrawOrderInfo.setUserId(loginUser.getId());
        List<CashRecodeInfo> orders = this.withdrawOrderDao.queryList(withdrawOrderInfo);
        if (orders.size() > 0) {
            List<CashRecodeInfo> list = orders.stream().map(order -> {
                String s = order.getCreatedTime().substring(0, 19);
                order.setCreatedTime(s);
                return order;
            }).collect(Collectors.toList());
            PageInfo<CashRecodeInfo> pageInfo = new PageInfo<CashRecodeInfo>(orders);

            PageData<CashRecodeInfo> pageData = new PageData<CashRecodeInfo>(pageInfo.getTotal(), list);

            return pageData;
        } else {
            PageInfo<CashRecodeInfo> pageInfo = new PageInfo<CashRecodeInfo>(orders);

            PageData<CashRecodeInfo> pageData = new PageData<CashRecodeInfo>(pageInfo.getTotal(), orders);

            return pageData;
        }

    }


    /**
     * 查询用户余额
     *
     * @param loginUser
     * @return
     */
    @Override
    public Map Account(UserInfo loginUser) {
        UserAccount record = new UserAccount();
        record.setUserId(loginUser.getId());
        UserAccount account = this.userAccountDao.queryUserAccount(record);
        if (account != null) {
            Map<String, Long> map = new HashMap<>();
            map.put("account", account.getAccount());
            return map;
        } else {
            Map<String, Long> map = new HashMap<>();
            map.put("account", 0L);
            return map;
        }

    }


    /**
     * 发起提现
     *
     * @param loginUser
     * @param
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultMessage withdraw(UserInfo loginUser, double money) throws Exception {
        //把金额转换成Long类型
        Long account = (Long) Account(loginUser).get("account");
        double count = account / 100;
        if (count < money) {
            //余额不足
            return ResultMessage.createFail("您的余额不足!!!");
        }
        //准备提现参数
        WithdrawParam withdrawParam = new WithdrawParam();
        withdrawParam.setNotifyURL(callbackUrl);


        //查找oem的一麻袋提现账户配置
        OemAccountConfig oemAccountrecord = new OemAccountConfig();
        oemAccountrecord.setOemId(loginUser.getOemId());
        OemAccountConfig oemAccountConfig = this.oemAccountConfigDao.queryOemAccountConfig(oemAccountrecord);
        if (oemAccountConfig == null) {
            return ResultMessage.createFail("请联系oem商设置提现配置");
        }

        //判断oem是否开启提现开关
        if (oemAccountConfig.getEnable() == null || oemAccountConfig.getEnable() == 0) {
            return ResultMessage.createFail("暂时未开启提现功能,请联系管理员!");
        }

        //小于最小提现金额
        if (oemAccountConfig.getMin_amount() > money) {
            return ResultMessage.createFail("您的提现金额小于最小提现金额");
        }

        withdrawParam.setAccountNumber(oemAccountConfig.getAccountNumber());

        //设置提现人基本参数
        UserBankInfo userBankInfoRecord = new UserBankInfo();
        userBankInfoRecord.setUserId(loginUser.getId());
        UserBankInfo userBankInfo = this.userBankInfoDao.queryUserBankInfo(userBankInfoRecord);
        Map<String, StringBuffer> validateMap = ValidatorUtil.validate(userBankInfo);
        if (userBankInfo == null || !validateMap.isEmpty()) {
            //用户还没设置银行卡信息
            return ResultMessage.createFail("请先完善银行卡信息");
        }

        //今天是否有成功发起的提现请求
        if (IsHaveWithdrawalRecord(loginUser)) {
            //有提现不能再发起请求了
            return ResultMessage.createFail("一天只能发起一次提现");
        }


        //生成一条提现订单记录
        CashRecodeInfo withdrawOrderInfo = new CashRecodeInfo();
        createWithdraw(loginUser, money, withdrawOrderInfo, userBankInfo);

        //用户提现订单信息参数填写
        TransferList transferList = new TransferList();
        createTransferParam(loginUser, money, withdrawParam, oemAccountConfig, userBankInfo, withdrawOrderInfo, transferList);

        if (StringUtils.isBlank(transferList.getSecureCode())) {
            //私钥设置有问题
            log.error("私钥设置有问题 oemId:{}", loginUser.getOemId());
            return ResultMessage.createFail("暂不能提现,请联系管理员设置提现配置");
        }
        withdrawParam.setTransferList(transferList);
        //生成最终参数
        String xml_parm = WithdrawUtil.object2xml(withdrawParam);
        String transData = Base64Utils.encode(xml_parm);
        log.info("推送到提现服务器>>>>>>>xml:" + xml_parm);
        Map<String, Object> inParam = new HashMap<>();
        inParam.put("url", url);
        inParam.put("transData", transData);
        String resutStr = HttpClientUtil.doPost(inside_outapi + "/withdraw/order", inParam);
        Map<String, Object> resutMap = JSON.parseObject(resutStr);
        String result_str = (String) resutMap.get("result");
        String xml_result = Base64Utils.decode(result_str);
        log.info("提现服务器返回结果<<<<<<<xml:" + xml_result);
        //记录接口调用日志
        createdLog(withdrawOrderInfo, xml_parm, xml_result);
        //开始解析返回结果
        Map<String, String> map_result = WithdrawUtil.xmlToMap(xml_result);
        String errCode = map_result.get("errCode");
        if ("0000".equals(errCode)) {
            //订单推送成功>>>更新订单状态
            CashRecodeInfo updataOrder = new CashRecodeInfo();
            updataOrder.setId(withdrawOrderInfo.getId());
            updataOrder.setErrCode("0000");
            updataOrder.setResCode(map_result.get("resCode"));
            updataOrder.setMessage("正在处理中");
            updataOrder.setSucceed("-11");
            this.withdrawOrderDao.update(updataOrder);
            if ("0000".equals(map_result.get("resCode"))) {
                //订单处理成功进行扣款操作
                HashMap<String, Object> userAccoountParm = new HashMap<>();
                userAccoountParm.put("userId", loginUser.getId());
                userAccoountParm.put("money", -withdrawOrderInfo.getAmount());
                this.userAccountDao.update(userAccoountParm);
            } else {
                //订单处理失败
                return ResultMessage.createFail(map_result.get("resCode"));
            }
        } else {
            //订单推送失败
            CashRecodeInfo updataOrderErr = new CashRecodeInfo();
            updataOrderErr.setId(withdrawOrderInfo.getId());
            updataOrderErr.setErrCode(errCode);
            updataOrderErr.setMessage("订单推送失败");
            updataOrderErr.setSucceed("-11");
            this.withdrawOrderDao.update(updataOrderErr);
            return ResultMessage.createFail("暂不能提现,请联系管理员 errCode:"+errCode);
        }

        return ResultMessage.createSuccess();
    }

    /**
     * 查看当天是否有提现记录
     *
     * @param loginUser
     * @return
     */
    private Boolean IsHaveWithdrawalRecord(UserInfo loginUser) {
        String todaySatartTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd 00:00:00");
        CashRecodeInfo todayCashRecord = new CashRecodeInfo();
        todayCashRecord.setUserId(loginUser.getId());
        todayCashRecord.setStartTime(todaySatartTime);
        List<CashRecodeInfo> list = this.withdrawOrderDao.queryList(todayCashRecord);
        if (list.size() > 0) {
            return true;
        } else {
            return false;
        }
    }


    /**
     * 组织用户提现参数
     *
     * @param loginUser
     * @param money
     * @param withdrawParam
     * @param oemAccountConfig
     * @param userBankInfo
     * @param withdrawOrderInfo
     * @param transferList
     * @throws Exception
     */
    private void createTransferParam(UserInfo loginUser, double money, WithdrawParam withdrawParam, OemAccountConfig oemAccountConfig, UserBankInfo userBankInfo, CashRecodeInfo withdrawOrderInfo, TransferList transferList) throws Exception {
        transferList.setBankCode(userBankInfo.getBankCode());
        transferList.setProvice(userBankInfo.getProvice());
        transferList.setCity(userBankInfo.getCity());
        transferList.setCardNo(userBankInfo.getCardNo());
        transferList.setAccountName(userBankInfo.getAccountName());
        double cost = creatCost(money);
        transferList.setAmount(MathUtil.sub(money, cost));
        transferList.setTransId(withdrawOrderInfo.getId());

        //remak备注信息
        transferList.setRemark("提现userId:" + loginUser.getId() + ",orderId:" + withdrawOrderInfo.getId());
        /**
         * 签名信息(明文)
         * transId=transId&accountNumber=accountNumber&cardNo=cardNo&amount=amount
         */
        String plain = "transId=" + transferList.getTransId() + "&accountNumber=" + withdrawParam.getAccountNumber() + "&cardNo=" + transferList.getCardNo() + "&amount=" + transferList.getAmount();
        //进行私钥加密
        String privateKey = oemAccountConfig.getPrivateKey();
        HcEncodeRsaUtils rsaUtils = HcEncodeRsaUtils.getInstance();
        String signInfo = rsaUtils.signData(plain, privateKey);
        transferList.setSecureCode(signInfo);
    }


    /**
     * 创建一条提现记录
     *
     * @param loginUser
     * @param money
     * @param withdrawOrderInfo
     */
    private void createWithdraw(UserInfo loginUser, double money, CashRecodeInfo withdrawOrderInfo, UserBankInfo userBankInfo) {
        withdrawOrderInfo.setAccountName(userBankInfo.getAccountName());
        withdrawOrderInfo.setCardNo(userBankInfo.getCardNo());
        withdrawOrderInfo.setUserId(loginUser.getId());
        withdrawOrderInfo.setOemId(loginUser.getOemId());
        long amount = (long) (money * 100);
        //计算费率
        double cost = creatCost(money);
        withdrawOrderInfo.setAmount(amount);
        withdrawOrderInfo.setActualAmount(amount - (long) (cost * 100));
        withdrawOrderInfo.setCost((long) (cost * 100));
        withdrawOrderInfo.setSucceed("-11");
        withdrawOrderInfo.setCreatedBy(loginUser.getId());
        withdrawOrderInfo.setUpdatedBy(loginUser.getId());
        withdrawOrderInfo.setMessage("订单推送中");
        this.withdrawOrderDao.insert(withdrawOrderInfo);
    }

    /**
     * 记录接口日志
     *
     * @param withdrawOrderInfo
     * @param xml_parm
     * @param xml_result
     */
    private void createdLog(CashRecodeInfo withdrawOrderInfo, String xml_parm, String xml_result) {
        InterfaceRequestLog lg = new InterfaceRequestLog();
        lg.setBiz(1);
        lg.setBiz_id(withdrawOrderInfo.getId());
        lg.setInterface_id(1);
        lg.setOut_param(xml_parm);
        lg.setIn_param(xml_result);
        lg.setStatus(1);
        this.interfaceRequestLogDao.insert(lg);
    }

    /**
     * 计算费率
     *
     * @param money
     * @return
     */
    private double creatCost(double money) {
        double value = (money * 0.07) + 1;
        long round = Math.round(value * 100);//保留两位小数四舍五入
        double cost = round / 100.0;
        return cost;
    }

}
